package com.unlcn.ils.kas.service.order.impl;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.toolkit.CollectionUtils;
import com.unlcn.ils.kas.dao.mapper.onway.GeelyOnWayMapper;
import com.unlcn.ils.kas.dao.mapper.onway.GeelyServiceCallGmtMapper;
import com.unlcn.ils.kas.dao.model.onway.GeelyOnWay;
import com.unlcn.ils.kas.dao.model.onway.GeelyServiceCallGmt;
import com.unlcn.ils.kas.dao.model.onway.bo.GeelyOnWayBo;
import com.unlcn.ils.kas.service.bo.GeelyServiceResponse;
import com.unlcn.ils.kas.service.constant.GeelyServiceConfiger;
import com.unlcn.ils.kas.service.order.GeelyService;
import com.unlcn.ils.kas.service.util.HttpClientUtil;

import org.apache.http.protocol.HTTP;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

/*
 * @Description: Geely服务层
 * @Author: zharui
 * */
@Service
public class GeelyServiceImpl implements GeelyService {
  private final static Logger logger = Logger.getLogger(GeelyServiceImpl.class);

  private static HashMap<String, String> pmap;

  static {
    pmap = new HashMap<String, String>();
    pmap.put("京", "BJ");
    pmap.put("沪", "SH");
    pmap.put("津", "TJ");
    pmap.put("渝", "CQ");
    pmap.put("冀", "HE");
    pmap.put("晋", "SX");
    pmap.put("蒙", "NM");
    pmap.put("辽", "LN");
    pmap.put("吉", "JL");
    pmap.put("黑", "HL");
    pmap.put("苏", "JS");
    pmap.put("浙", "ZJ");
    pmap.put("皖", "AH");
    pmap.put("闽", "FJ");
    pmap.put("赣", "JX");
    pmap.put("鲁", "SD");
    pmap.put("豫", "HA");
    pmap.put("鄂", "HB");
    pmap.put("湘", "HN");
    pmap.put("粤", "GD");
    pmap.put("桂", "GX");
    pmap.put("琼", "HI");
    pmap.put("川", "SC");
    pmap.put("贵", "GZ");
    pmap.put("滇", "YN");
    pmap.put("藏", "XZ");
    pmap.put("陕", "SN");
    pmap.put("甘", "GS");
    pmap.put("青", "QH");
    pmap.put("宁", "NX");
    pmap.put("新", "XJ");
    pmap.put("台", "TW");
    pmap.put("港", "HK");
    pmap.put("澳", "MO");
  }

  @Autowired
  private GeelyServiceConfiger configer;

  @Autowired
  private GeelyServiceCallGmtMapper callGmtMapper;

  @Autowired
  private GeelyOnWayMapper onwayInfoMapper;

  /*
   * @Description: 上传自上次调用接口时间开始未推送过的在途信息
   * 
   * @Author: zharui
   */
  @Override
  public int uploadOnWayUnpushed() {
    int ret = 0;
    Date now_time = new Date();
    Date last_call_time = callGmtMapper.getLastCallTimeByPath(configer.getUploadOnWayPath());
    List<GeelyOnWay> infos = onwayInfoMapper.selectUnpushed(last_call_time);
    if (CollectionUtils.isNotEmpty(infos)) {
      List<GeelyOnWayBo> onways = transformatOnWayInfos(infos);
      logger.info("uploadOnWayUnpushed onways size is ... " + onways.size());
      if (CollectionUtils.isNotEmpty(onways)) {
        GeelyServiceResponse resp = null;
        int loops = countLoop(onways);
        logger.info("uploadOnWayUnpushed loop " + loops + " times");
        for (int i = 0; i < loops; i++) {
          List<GeelyOnWayBo> subSet = onways.subList(configer.getUploadUpperValve() * i,
              Math.min(configer.getUploadUpperValve() * (i + 1), onways.size()));
          try {
            resp = upload(subSet);
          } catch (Exception e) {
            logger.error("uploadOnWay ...", e);
            resp = null;
          }
          if (null != resp && resp.isSuccess()) {
            ret = ret + 1;
            ArrayList<String> vinList = new ArrayList<String>();
            for (GeelyOnWayBo bo : subSet) {
              vinList.add(bo.getVin());
            }
            if (!vinList.isEmpty()) {
              onwayInfoMapper.confirmPushedBatch(vinList, now_time);
            }

          } else if (null != resp && !resp.isSuccess()) {
            logger.error("uploadOnWay ... response error " + resp.getErrorMessage());
          } else {
            logger.error("uploadOnWay ... response error 非标准反馈");
          }
          // 单次调用后延迟若干毫秒
          if (configer.getUploadDelayInMilliSecond() > 0) {
            try {
              Thread.sleep(configer.getUploadDelayInMilliSecond());
            } catch (InterruptedException e) {
              logger.error("uploadOnWay ... sleep : ", e);
            }
          }
        }
        writeLastCall(configer.getUploadOnWayPath(), now_time);
      }
    }
    return ret;
  }

  /*
   * @Description: 上传动作
   * 
   * @Author: zharui
   */
  public GeelyServiceResponse upload(List<GeelyOnWayBo> onwayList) throws Exception {
    if (onwayList.isEmpty()) {
      throw new IllegalArgumentException("在途列表为空");
    }
    if (onwayList.size() > configer.getUploadUpperValve()) {
      throw new IllegalArgumentException("在途列表长度超出上限" + configer.getUploadUpperValve());
    }
    String uploadURL = configer.getBaseUrl() + configer.getUploadOnWayPath();
    String authData = assembleAuthData();
    String username = configer.getUsername();
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    Date now = new Date();
    HashMap<String, String> params = new HashMap<String, String>();
    params.put("authdata", authData);
    params.put("username", username);
    params.put("date", sdf.format(now));
    params.put("infos", JSON.toJSONString(onwayList, SerializerFeature.BrowserCompatible));
    HashMap<String, String> addHeaders = new HashMap<String, String>();
    addHeaders.put(HTTP.CONTENT_TYPE, configer.getContentType());

    String response = null;

    response = HttpClientUtil.post(uploadURL, params, addHeaders, 10000);

    if (!StringUtils.isEmpty(response)) {
      logger.info("uploadOnWay ... response:" + response);
      if (response.contains("500 Internal Server")) {
        throw new IllegalAccessException("Nginx 500 Internal Server Error");
      } else {
        try {
          GeelyServiceResponse resp = JSON.parseObject(response, GeelyServiceResponse.class);
          return resp;
        } catch (Exception e) {
          e.addSuppressed(new IllegalAccessException("吉利服务返回意外错误"));
          throw e;
        }
      }
    } else {
      throw new Exception("上传吉利在途信息时无返回");
    }
  }

  /*
   * @Description: 写入接口最后调用时间
   * 
   * @Author: zharui
   */
  public void writeLastCall(String url, Date call_time) {
    GeelyServiceCallGmt entity = new GeelyServiceCallGmt();
    entity.setLast_call_time(call_time);
    entity.setUrl(url);

    Date dt = callGmtMapper.getLastCallTimeByPath(url);
    if (null == dt) {
      callGmtMapper.insert(entity);
    } else {
      callGmtMapper.update(entity, null);
    }
  }

  /*
   * @Description: 在途明细格式转换,按vin拼装
   * 
   * @Author: zharui
   */
  public List<GeelyOnWayBo> transformatOnWayInfos(List<GeelyOnWay> infos) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    SimpleDateFormat sdfLong = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    ArrayList<GeelyOnWayBo> ret = new ArrayList<GeelyOnWayBo>();
    HashMap<String, GeelyOnWayBo> map = new HashMap<String, GeelyOnWayBo>();
    if (!infos.isEmpty()) {
      for (GeelyOnWay info : infos) {
        if (null != info) {
          if (!map.containsKey(info.getVin())) {
            GeelyOnWayBo bo = new GeelyOnWayBo();
            bo.setTransportSn(info.getCust_ship_no());
            bo.setVin(info.getVin());
            bo.setGpsSn(virtualGpsSN(info.getTruck()));
            bo.setTransportNum(info.getTruck());
            bo.setTransportMethord(GeelyOnWayBo.translateTransportMethord(info.getTransport_type()));
            bo.setDate(sdf.format(info.getLocation_time()));
            List<GeelyOnWayBo.Position> posLst = new ArrayList<GeelyOnWayBo.Position>();
            GeelyOnWayBo.Position pos = bo.new Position();
            pos.setLongitude(info.getLongitude());
            pos.setLatitude(info.getLatitude());
            pos.setProvince(info.getProvince());
            pos.setCity(info.getCity());
            pos.setAddress(info.getAddress());
            pos.setDateTime(sdfLong.format(info.getLocation_time()));
            posLst.add(pos);
            bo.setPositions(posLst);
            map.put(info.getVin(), bo);
          } else {
            GeelyOnWayBo bo = map.get(info.getVin());
            List<GeelyOnWayBo.Position> posLst = bo.getPositions();
            GeelyOnWayBo.Position pos = bo.new Position();
            pos.setLongitude(info.getLongitude());
            pos.setLatitude(info.getLatitude());
            pos.setProvince(info.getProvince());
            pos.setCity(info.getCity());
            pos.setAddress(info.getAddress());
            pos.setDateTime(sdfLong.format(info.getLocation_time()));
            posLst.add(pos);
            bo.setPositions(posLst);
            map.put(info.getVin(), bo);
          }
        }
      }
      Iterator<String> itor = map.keySet().iterator();
      while (itor.hasNext()) {
        String vin = itor.next();
        ret.add(map.get(vin));
      }
    }
    return ret;
  };

  /*
   * @Description: 生成授权信息
   * 
   * @author: zharui
   */
  @Override
  public String assembleAuthData() throws NoSuchAlgorithmException {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
    StringBuilder sb = new StringBuilder().append(configer.getAppKey()).append(configer.getAppSecret())
        .append(sdf.format(new Date()));
    return encryptByMD5(sb.toString());
  }

  /*
   * @Description: 计算循环推送次数
   * 
   * @Author: zharui
   */
  public int countLoop(List<GeelyOnWayBo> onways) {
    double b = new Double(onways.size());
    return (int) Math.ceil(b / configer.getUploadUpperValve());
  }

  /*
   * @Description: 从车牌号虚拟gps设备号,虚拟规则不影响数据传输
   * 
   * @author: zharui
   */
  public String virtualGpsSN(String truckNum) {
    if (!StringUtils.isEmpty(truckNum) && truckNum.length() > 1) {
      String provinceKey = truckNum.substring(0, 1);
      String tailer = truckNum.substring(1);
      if (pmap.containsKey(provinceKey)) {
        return pmap.get(provinceKey) + tailer;
      } else {
        return "VT" + tailer;
      }
    } else {
      throw new IllegalArgumentException("承运车牌号最小长度必须大于1");
    }
  }

  /*
   * MD5加密
   */
  private static String encryptByMD5(String unencrypt) throws NoSuchAlgorithmException {
    String ret;
    // 生成一个MD5加密计算摘要
    MessageDigest md;
    try {
      md = MessageDigest.getInstance("MD5");
      // 计算md5函数
      md.update(unencrypt.getBytes());
      // digest()最后确定返回md5 hash值，返回值为8为字符串。因为md5 hash值是16位的hex值，实际上就是8位的字符
      // 将8位的字符串转换成16位hex值，用字符串来表示；得到字符串形式的hash值
      byte[] hash = md.digest();
      StringBuilder securty = new StringBuilder();
      for (int i = 0; i < hash.length; i++) {
        int v = hash[i] & 0xFF;
        if (v < 16)
          securty.append(0);
        securty.append(Integer.toString(v, 16));
      }
      ret = securty.toString();
    } catch (NoSuchAlgorithmException e) {
      throw e;
    }
    return ret;
  }
}